import java.io.*;
import java.util.Scanner;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import java.util.Calendar;
import java.text.SimpleDateFormat;

public class RinexFile {

    // Need to know when we have finished reading the header 
    private static int eohFlag = 0;
    private static int obsTimeFlag = 1;
    private static int getSatsFlag = 0;
    private static int numObs = 0;
    private static int epochNumObs = 0;
    private static int epoch = 0;
    private static int numObsTypes = 0;
    private static int numOfLines = 0;
    private static int numOfLinesRead = 0;

    private static StringUtils util;
    private static EpochData[] epochDataArray;// = new EpochData[3600];
    private static EpochData epochInfo;
    
    private static long firstEpoch = 0;
    private static long lastEpoch  = 0;

    private static RinexObsHeader header;
    //=====================================================
    // Pattern matching variable for Satellites Observed
    private static dsSatsObserved gpsSatsObserved     = new dsSatsObserved(32);
    private static dsSatsObserved glonassSatsObserved = new dsSatsObserved(32);
    private static Pattern p = Pattern.compile("\\d+");
    private static String glonassConstell = "R";
    private static String gpsConstell = "G";

    private static dsSatelliteData dsSatData;// = new dsSateliiteData(totalSize,totalEpochs);
    //============================================================
    // Get a File from stdin
    //============================================================
    public static void main(String[] args) {
        if(args.length == 1 ) {
           ////System.out.println("Got an argument!");
           //firstFile = args[0];
        }
        else {
            System.err.println("usage: java RinexFile file_path");
            System.exit(1);
        }
        
        String file = args[0]; 
        int type = checkFileType(file);
        if(type == 0) {
            System.exit(0);
        }
        else if(type == 1) {
            readObsFile( file );

            // check to see if these is a GPS navigation file in the same location
            String nFile = file.substring(0,file.length()-1) + 'n';
            //System.out.println("Looking for a file called "+nFile);
            File n = new File(nFile);
            if(n.exists()) {
                RinexGPSNavFile gpsNavFile = new RinexGPSNavFile(nFile);
                gpsNavFile.readGPSNavFile();
                /*
                int nDatIndex = gpsNavFile.findNavData(25,1000);
                NavDataGPS nData = gpsNavFile.getNavData(nDatIndex);
                System.out.println("We have a match :"+nData.prn()+":"+nDatIndex);
                nData.printRecord();
                gpsNavFile.findNavData(25);
                long time = dsSatData.getTime(25, 0 );
                System.out.println("Epoch Time at 0 :"+time );
                double P1 = dsSatData.getP1( 25, 0 );
                System.out.println("P1 at epoch 0   :"+P1);

                time = dsSatData.getTime(25, 1 );
                System.out.println("Epoch Time at 1 :"+time );
                P1 = dsSatData.getP1( 25, 1 );
                System.out.println("P1 at epoch 1   :"+P1);

                time = dsSatData.getTime(25, 2 );
                System.out.println("Epoch Time at 2 :"+time );
                P1 = dsSatData.getP1( 25, 2 );
                System.out.println("P1 at epoch 2   :"+P1);
                
                time = dsSatData.getTime(25, 3 );
                System.out.println("Epoch Time at 3 :"+time );
                P1 = dsSatData.getP1( 25, 3 );
                System.out.println("P1 at epoch 3   :"+P1);

                time = dsSatData.getTime(25, 4 );
                System.out.println("Epoch Time at 4 :"+time );
                P1 = dsSatData.getP1( 25, 4 );
                System.out.println("P1 at epoch 4   :"+P1);

                time = dsSatData.getTime(25, 5 );
                System.out.println("Epoch Time at 5 :"+time );
                P1 = dsSatData.getP1( 25, 5 );
                System.out.println("P1 at epoch 5   :"+P1);
                */
            }

            // Now check if there is a GLONASS Nav File to read as well 
            String gFile = file.substring(0,file.length()-1) + 'g';
            //System.out.println("Looking for a file called "+gFile);
            File g = new File(gFile);
            if(g.exists()) {
                RinexGLONASSNavFile glonassNavFile = new RinexGLONASSNavFile(gFile);
                glonassNavFile.readGLONASSNavFile();
            }
            
            //CalcGPSSatPosition = calc new calcGPSSatPosition();
            //calc.

            //int tPRN = 25;
            //String tConstell = "G";
            //System.out.println("About to dump SN1 values");
            //int k = header.obs2index("C1");
            //dsSatData.dumpObs(tPRN,tConstell,k);
        }
        else if(type == 2) {
            RinexGPSNavFile gpsNavFile = new RinexGPSNavFile(file);
            gpsNavFile.readGPSNavFile();
            // Should print a summary of the Navigation File
            // -what satellites are healthy, accuracy, other facts
            System.exit(0);
        }
        else if(type == 3) {
            RinexGLONASSNavFile glonassNavFile = new RinexGLONASSNavFile(file);
            glonassNavFile.readGLONASSNavFile();
            // Should print a summary of the Navigation File
            // -what satellites are healthy, accuracy, other facts
            System.exit(0);
        }
         
    }

    //
    // Read the first line of the RINEX file
    //
    private static int checkFileType(String fileName) {
       Pattern obsFile = Pattern.compile("OBSERVATION\\sDATA");
       Pattern gpsNavFile = Pattern.compile("GPS\\sNAV\\sDATA");
       Pattern glonassNavFile = Pattern.compile("GLONASS\\sNAV\\sDATA");
       
       try {
           File file = new File(fileName);
           FileReader reader = new FileReader(file);
           BufferedReader in = new BufferedReader(reader);
           String line;
       
           while ( (line = in.readLine()) != null) {
                Matcher m_obsFile = obsFile.matcher(line);
                if( m_obsFile.find() ) {
                    System.out.println("We have a RINEX OBS file");
                    return 1;
                }

                Matcher m_gpsNavFile = gpsNavFile.matcher(line);
                if ( m_gpsNavFile.find() ) {
                    System.out.println("We have a RINEX GPS Navigation File");
                    return 2;
                }

                Matcher m_glonassNavFile = glonassNavFile.matcher(line);
                if ( m_glonassNavFile.find() ) {
                    System.out.println("We have a RINEX GLONASS Navigation File");
                    return 3;
                }
                else {
                    System.err.println("Have no idea what your trying to give me crap in = crap out");
                    return 0;
                }
       
    
           }
           in.close(); 
       }
       catch(IOException e) {
           e.printStackTrace();
       }
       return 0;
    }

    private static void readObsFile(String fileName) {

    //
    // Do a quick first pass of the RINEX file to get some basic meta data
    // number of observations per satellite
    // start time, end time
    //
       try {
           File file = new File(fileName);
           FileReader reader = new FileReader(file);
           BufferedReader in = new BufferedReader(reader);
           String line;

           header = new RinexObsHeader();

           while ( (line = in.readLine()) != null) {
                // Read in the header details
                if(eohFlag == 0 ) {
                    header.parseHeaderLine(line);
                    if( header.checkEOH(line) ) {
                        eohFlag = 1;
                    }
                }
                else {
                    if(obsTimeFlag == 1 ) {
                        epochInfo = new EpochData();
                        epochInfo.parseObsTime(line);
                        getSatsFlag = epochInfo.getSats(line);
                        obsTimeFlag = 0;
                        int numSats = epochInfo.getNumSats();
                        //Work out how many lines of observations should be read
                        if( header.numObsTypes() > 5 && header.numObsTypes() < 10 ) {
                            numOfLines = 2 * numSats;
                        }
                        else if(header.numObsTypes() > 10 ) {
                            numOfLines = 3 * numSats;
                        }
                        else {
                            numOfLines = numSats;
                        }

                        if(epoch > 0 ) {
                            lastEpoch = epochInfo.getTime();
                        }
                        else {
                            firstEpoch = epochInfo.getTime();
                        }
                    }
                    // There might be more than one line of sats
                    else if(getSatsFlag > 0) {
                        getSatsFlag = epochInfo.getSats(line);
                    }
                    else {
                        numOfLinesRead++;

                        if(numOfLinesRead == numOfLines) {
                            obsTimeFlag = 1;
                            numOfLinesRead = 0;
                            epoch++;
                            int numSats = epochInfo.getNumSats();
                            // look for unique satellites and store them in a list
                            for(int j=0 ; j < numSats; j++) {
                                int satPRN = epochInfo.getPRN(j);
                                String constell = epochInfo.getConstell(j);
                                if( constell.matches(glonassConstell) ) {
                                    glonassSatsObserved.add(satPRN);
                                }
                                else {
                                    gpsSatsObserved.add( satPRN );
                                }
                            }

                        }
                    }
                }
            }
            in.close();

            //==========================================================================
            //
            // Print out some of the basic meta data we've found out from the first pass
            //
            //==========================================================================
            int totalEpochs = epoch;
            System.out.println("");
            System.out.println("========================================================");
            System.out.println("Meta data from                       : "+file);
            System.out.println("4 Character ID                       : ");
            System.out.println("DOMES number                         : ");
            System.out.println("Receiver Type                        : ");
            System.out.println("Antenna Type                         : ");
            System.out.println("Observation Interval                 : ");
            System.out.println("First epoch                          : "+firstEpoch); // put this in DoY
            System.out.println("Last  epoch                          : "+lastEpoch);  // format
            System.out.println("Total Number of Epochs               : "+totalEpochs);
            System.out.println("Total Number of Satellites Observed  : ");
            System.out.println("Total Number of GPS Sats Tracked     : "+gpsSatsObserved.numSats());
            System.out.println("Total Number of GLONASS Sats Tracked : "+glonassSatsObserved.numSats());
            System.out.println("");

            System.out.println("GPS Satellites Observed");
            System.out.println();
            gpsSatsObserved.dumpValues();
            System.out.println();
            System.out.println("GLONASS Satellites Observed");
            System.out.println();
            glonassSatsObserved.dumpValues();
            System.out.println();
            System.out.println("========================================================");
            System.out.println("");
        
            header.printHeaderMetaData();
            //==========================================================================
            //
            // Now read in the file for the second time and parse the data
            //
            //==========================================================================
            reader = new FileReader(file);
            BufferedReader br = new BufferedReader(reader);
            eohFlag = 0;
            epoch = 0;

            int numSats = 0;
            int satObsParsed = 0;
            int c = 0; // iterator to indicate when we've moved to another sat
            int maxC = 1; // number of lines to be read for each satellite
            //
            // Initialise the data structures to hold the data for each satellite
            //
            int totalSats = gpsSatsObserved.numSats() + glonassSatsObserved.numSats();
            dsSatData = new dsSatelliteData(totalSats,totalEpochs);

            for(int k = 1; k <= 32; k++) {
                if( gpsSatsObserved.numEpochs(k) > 0 ) {
                    dsSatData.newSat(k,"G",gpsSatsObserved.numEpochs(k),header.numObsTypes()); 
                }
            }

            for(int k = 1; k <= 32; k++) {
                if( glonassSatsObserved.numEpochs(k) > 0 ) {
                    dsSatData.newSat(k,"R",glonassSatsObserved.numEpochs(k),header.numObsTypes()); 
                }
            }
            //dsSatData.dumpSatPrns();
           
            while ( (line = br.readLine()) != null) {
                // Read in the header details
                // should try and skip over the header
                if(eohFlag == 0 ) {
                    if( header.checkEOH(line) ) {
                        eohFlag = 1;
                    }
                }
                else {
                    if(obsTimeFlag == 1 ) {
                        epochInfo = new EpochData();
                        epochInfo.parseObsTime(line);
                        getSatsFlag = epochInfo.getSats(line);
                        obsTimeFlag = 0;
                        satObsParsed = 0;
                        numSats = epochInfo.getNumSats();
                        c = 0; // iterator to indicate when we've moved to another sat

                        // Work out how many lines of observations should be read
                        if( header.numObsTypes() > 5 && header.numObsTypes() < 10 ) {
                            numOfLines = 2 * numSats;
                            maxC = 2;
                        }
                        else if( header.numObsTypes() > 10 ) {
                            numOfLines = 3 * numSats;
                            maxC = 3;
                        }
                        else {
                            numOfLines = numSats;
                            maxC = 1;
                        }
                    }
                    // There might be more than one line of sats
                    else if(getSatsFlag > 0) {
                        getSatsFlag = epochInfo.getSats(line);
                    }
                    else {
                        int prn = epochInfo.getPRN( satObsParsed );
                        String constell = epochInfo.getConstell( satObsParsed );

                        dsSatData.setTime(prn,constell,epochInfo.getTime());
                        //System.out.println("Sat Obs Parsed:"+satObsParsed+" on satellite:"+prn);
                        epochInfo.parseSatData(line);
                        // Work out how many lines we have read, and make sure we archive it off 
                        // to the right data array
                        if(c == 0) {
                            int numIter = 5;
                            if( header.numObsTypes() < 5 ) { numIter = header.numObsTypes();}
                            
                            dsSatData.setTime(prn,constell,epochInfo.getTime());
                            for (int j = 0 ; j < numIter ; j++ ) { 
                                dsSatData.addData(prn,constell,epochInfo.getObs(j),j);
                                //System.out.print("PRN:"+prn+": OBS "+ j+" "+epochInfo.getObs(j)+"  ");
                            }
                            if(maxC > 1) { c++; }
                            else { 
                                c =0 ; 
                                satObsParsed++; 
                                dsSatData.incEpoch(prn,constell);
                                //dsSatData.setTime(prn,constell,epochInfo.getTime());
                            }
                        }
                        else if (c == 1 ) {
                            int numIter = 5;
                            if( header.numObsTypes() < 10 ) { numIter = header.numObsTypes() - 5;}
                            
                            for (int j = 0 ; j < numIter ; j++ ) { 
                                int off = j+5;
                                dsSatData.addData(prn,constell,epochInfo.getObs(j),off);
                            //    System.out.print("OBS "+off+" "+epochInfo.getObs(j)+"  ");
                            }
                            if(maxC > 2) { c++; }
                            else { 
                                c =0 ; 
                                satObsParsed++; 
                                dsSatData.incEpoch(prn,constell);
                                //dsSatData.setTime(prn,constell,epochInfo.getTime());
                            }
                        }
                        else if (c == 2) {
                            int numIter = 5;
                            if( header.numObsTypes() < 15 ) { numIter = header.numObsTypes() - 10;}
                            
                            for (int j = 0 ; j < numIter ; j++ ) { 
                                int off = j+10;
                                dsSatData.addData(prn,constell,epochInfo.getObs(j),off);
                            //    System.out.print("OBS "+off+" "+epochInfo.getObs(j)+"  ");
                            }

                            if(maxC > 3) { c++; }
                            else { 
                                c =0 ;
                                satObsParsed++; 
                                dsSatData.incEpoch(prn,constell);
                                //dsSatData.setTime(prn,constell,epochInfo.getTime());
                            }
                        }
                        numOfLinesRead++;

                        if(numOfLinesRead == numOfLines) {
                            obsTimeFlag = 1;
                            numOfLinesRead = 0;
                            epoch++;
                        }
                    }
                }
           }
           br.close(); 
       }
       catch(IOException e) {
           e.printStackTrace();
       }

    }
   
}



